#include "sockets.h"

// Semáforo del thread socket
extern sem_t mutex_socket;

// Semáforo del thread socket
extern sem_t mutex_socket_s;
extern sem_t socket_s_disp;

// Semáforos movimientos
extern sem_t nuevo_mov;
extern sem_t sem_mov;
extern sem_t mov_valido;

// Flag estado socket servidor (RECV o SEND)
int flag=0;

// Flag estado socket servidor (1-2 representa jugador)
int flag_j=-1;

// Sockets de clientes a manejar (server partida)
extern s_sockets socks;

void configurar_cliente(cl_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/client_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"IP = %s\nPUERTO = %d",arg->ip_srv,&(arg->puerto));
}

void configurar_servidor(sv_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/server_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"PUERTO = %d\nPARTIDAS = %d",&(arg->puerto),&(arg->cant_partidas));
}

void conectar_a_socket(int* sock_server,cl_config* configuracion)
{
	// Funcion que inicializa el socket
	struct hostent *he;
	struct sockaddr_in server;
	
	if((he = gethostbyname(HOST)) == NULL)
	{
		printf("Error de host: %s\n",strerror(errno));
		exit(1);
	}

	// Crear socket
	if((*sock_server = socket(AF_INET, SOCK_STREAM, 0))<0)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Definir propiedades del socket
	server.sin_family = AF_INET;
	server.sin_port = htons(configuracion->puerto);
	server.sin_addr.s_addr = ((struct in_addr *) (he->h_addr))->s_addr;
	bzero(&(server.sin_zero),8);

	printf("Conectando a %s:%d...\n",configuracion->ip_srv,configuracion->puerto);
	
	// Conectar
	if(connect(*sock_server, (struct sockaddr*) &server, sizeof(struct sockaddr_in)) == -1)
	{
		printf("Error en connect: %s\n",strerror(errno));
		close(*sock_server);
		exit(1);
	}	
	
	// Conectado
	printf("Se establecio la conexion con el servidor.\n");
}

void iniciar_socket(int* sock,sv_config* configuracion) 
{
	// Funcion que inicializa el socket del servidor por unica vez	
	int val=1;
	struct sockaddr_in srv;
	
	// Borrar
	struct sockaddr_in socketdir_cliente1,socketdir_cliente2; 
	int addrlen = sizeof( (struct sockaddr *) &socketdir_cliente1);
	
	// Crear socket
	if((*sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Definir propiedades del socket
	srv.sin_family = AF_INET;
	srv.sin_port = htons(configuracion->puerto);
	srv.sin_addr.s_addr = inet_addr(HOST);	
	//inet_aton("127.0.0.1", &srv.sin_addr.s_addr);

	// Reuse
	if(setsockopt(*sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(int)) == -1)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Vincular socket
	if(bind(*sock,(struct sockaddr*)&srv,sizeof(struct sockaddr)) <0)
	{
		printf("Error en bind: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}

	// Escuchar
	if(listen(*sock,2) <0)
	{
		printf("Error en listen: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}	
		
	// Espero clientes (esto no va acá)
	if((socks.socket1 = accept(*sock, (struct sockaddr *) &socketdir_cliente1, (socklen_t *) &addrlen)) == -1)
	{
		printf("Error en accept: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}	
			
	if((socks.socket2 = accept(*sock, (struct sockaddr *) &socketdir_cliente2, (socklen_t *) &addrlen)) == -1)
	{
		printf("Error en accept: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}
	sem_post(&nuevo_mov);
}

// Thread socket en cliente
void* thread_socket_c(void* arg)
{
	int socket,accion = -2;
	cl_config config;
	s_mov* mov = (s_mov*) arg;
		
	// Cargar configuración
	configurar_cliente(&config);
	
	printf("IP: %s | Puerto: %d\n",config.ip_srv,config.puerto);
	
	// Iniciar comunicación
	conectar_a_socket(&socket,&config);
		
	while(1)
	{
		// Espera señal
		printf("    <<< Socket espera señal\n");
		if(recv(socket,mov,sizeof(s_mov),0) <= 0)
		{
			printf("recv");
			exit(1);
		}
		
		// Hay nuevo movimiento
		sem_post(&nuevo_mov);
		
		// Si no hay ni perdedor ni ganador
		if(mov->estado == 0)
			do
			{
				printf("    <<< Socket espera por envío\n");
				// Bloqueo thread
				lock_socket();
				
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				// Envío movimiento al servidor
				if(send(socket,mov,sizeof(s_mov),0) == -1)
				{
					printf("sedirnd");
					exit(1);
				}
				
				printf("    <<< Socket envió pos: %d\n",mov->posicion);
				
				// Espera confirmación
				if(recv(socket,mov,sizeof(s_mov),0) <= 0)
				{
					printf("recv");
					exit(1);
				}
				
				accion = mov->accion;
				
				printf("    <<< Socket recibió validacion: %d\n",mov->accion);
				
				// Comunico validez a proceso cliente
				if(mov->accion != -1)
				{
					sem_post(&mov_valido);
					printf("    <<< Movimiento confirmado\n");
				}
					
				// Hay nuevo movimiento
				sem_post(&nuevo_mov);
				sem_post(&sem_mov);
				
			}while(accion == -1 || accion == 0);
			
		printf("    <<< Ciclo socket\n");
	}
	
	// Envío señal a pantalla para actualizar
	//pthread_kill(tid_pantalla,SIGUSR1);
}

// Thread socket en servidor
void* thread_socket_s(void* arg)
{
	int sock;
	int value=-1;
	
	sv_config config;
	s_mov* mov = (s_mov*) arg;
	
	// Cargar configuración
	configurar_servidor(&config);
	
	printf("Puerto: %d\n",config.puerto);

	// Iniciar comunicación
	iniciar_socket(&sock,&config);
	
	while(1)
	{
		// Espero desbloqueo
		lock_socket_s();
		
		// Mutex movimiento
		//sem_wait(&sem_mov);
		
		if(flag == CONF)					// Confirmación movimientos
		{
			printf("    <<< Confirmado para jug: %d...",flag_j);
			if(flag_j == 1)					// De jugador 1
			{
				if(send(socks.socket1,mov,sizeof(s_mov),0) == -1)
					exit(1);
			}
			
			else 										// De jugador 2
			{
				if(send(socks.socket2,mov,sizeof(s_mov),0) == -1)
					exit(1);
			}
			
			printf(" Confirmado (%d,%d,%d,%d)\n",mov->accion,mov->estado,mov->posicion,mov->n_posicion);
			flag=flag_j=-1;
		
			// Thread libre
			sem_post(&socket_s_disp);
		}
		
		else 											// Envío y recepción
		{
			printf("    <<< Envio/recepcion jug: %d... ",flag_j);
			if(flag_j == 1)					// A jugador 1
			{
				if(send(socks.socket1,mov,sizeof(s_mov),0) == -1)
					exit(1);
				printf("    <<< Envio (%d,%d,%d,%d)\n",mov->accion,mov->estado,mov->posicion,mov->n_posicion);
				if(recv(socks.socket1,mov,sizeof(s_mov),0) <= 0)
					exit(1);
			}
			
			else 										// A jugador 2
			{
				if(send(socks.socket2,mov,sizeof(s_mov),0) == -1)
					exit(1);
				printf("Envio (%d,%d,%d,%d)\n",mov->accion,mov->estado,mov->posicion,mov->n_posicion);
				if(recv(socks.socket2,mov,sizeof(s_mov),0) <= 0 -1)
					exit(1);
			}
			
			printf("    <<< Recibo posicion %d\n",mov->posicion);
			
			// A modo de confirmación
			sem_post(&nuevo_mov);	
			
			printf("    <<< loop\n");
			flag=flag_j=-1;
			
			// Thread libre
			sem_post(&socket_s_disp);
		}
		
		// Mutex movimiento
		sem_post(&sem_mov);
	}
}

// Sincronización socket server
void lock_socket_s()
{
	sem_wait(&mutex_socket_s);
}

void unlock_socket_s(int est, int jug)
{
	// Thread ocupado
	sem_wait(&socket_s_disp);
	
	flag = est;
	flag_j = jug;
	sem_post(&mutex_socket_s);
	printf("    <<< Socket_s unlocked, flag: %d\n",flag);
}

// Sincronización socket cliente
void lock_socket()
{
	sem_wait(&mutex_socket);
}

void unlock_socket()
{
	sem_post(&mutex_socket);
	printf("    <<< Socket_s locked\n");
}
